﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

using GDIBitmap = System.Drawing.Bitmap;

using XRpgLibrary.TileEngine;
using XRpgLibrary.ExtensionMethods;

namespace XLevelEditor
{
    public partial class FormExport : Form
    {
        const int MAX_TEXTURE_SIZE = 2048; //limitation of the Reach profile of the XNA 4 framework

        private GraphicsDevice graphics;
        private List<Tileset> tilesets;

        private SpriteBatch spriteBatch;
        private SpriteBatch SpriteBatch
        {
            get
            {
                if (spriteBatch == null)
                    spriteBatch = new SpriteBatch(graphics);

                return spriteBatch;
            }
        }

        private List<MapLayer> layers;

        public FormExport(List<MapLayer> layers, List<Tileset> tilesets, GraphicsDevice g)
        {
            InitializeComponent();

            this.graphics = g;
            this.layers = layers;
            this.tilesets = tilesets;
        }

        private void btnOK_Click(object sender, EventArgs e)
        {
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                Texture2D[,] map = GenerateMapImages(tilesets, cbCharacters.Checked, cbCharacterLabels.Checked);

                SaveChunksSeparately(map);
                SaveCombinedMap(map);
            }
        }

        private void SaveCombinedMap(Texture2D[,] map)
        {
            GDIBitmap image = Combine(
                map,
                layers[0].Width * Engine.TileWidth,
                layers[0].Height * Engine.TileHeight);

            using (Stream stream = File.OpenWrite(sfd.FileName))
            {
                image.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
            }
        }

        /// <summary>
        /// This is just for debnugging the inividual chunks before they get stitched
        /// </summary>
        /// <param name="map"></param>
        private void SaveChunksSeparately(Texture2D[,] map)
        {
            for (int y = 0, ycnt = map.GetLength(0); y < ycnt; y++)
                for (int x = 0, xcnt = map.GetLength(1); x < xcnt; x++)
                    using (Stream stream = File.OpenWrite(sfd.FileName.Insert(sfd.FileName.Length - 4, y.ToString() + x.ToString())))
                    {
                        map[y, x].ToImage().Save(stream, System.Drawing.Imaging.ImageFormat.Png);
                    }
        }

        /// <summary>
        /// This function exists because of a limitation in the framework. If Texture2D could be an unlimited size
        /// then GenerateMapImage would be fine on its own. But as it is, any map over 64 tiles wide or tall will
        /// choke that method because the maximum texture size is 2048 pixels. 
        /// 
        /// This function renders the map in chunks and returns a normal Bitmap which can be anysize.
        /// </summary>
        /// <param name="renderCharacters"></param>
        /// <param name="renderCharacterLabels"></param>
        /// <param name="tilesets"></param>
        /// <returns></returns>
        private Texture2D[,] GenerateMapImages(List<Tileset> tilesets, bool renderCharacters, bool renderCharacterLabels)
        {
            int totalWidth = layers[0].Width * Engine.TileWidth;
            int totalHeight = layers[0].Height * Engine.TileHeight;
            int chunksX = (int)Math.Ceiling((double)totalWidth / MAX_TEXTURE_SIZE);
            int chunksY = (int)Math.Ceiling((double)totalHeight / MAX_TEXTURE_SIZE);

            Texture2D[,] chunks = new Texture2D[chunksY, chunksX];

            for (int y = 0, ycnt = chunks.GetLength(0); y < ycnt; y++)
                for (int x = 0, xcnt = chunks.GetLength(1); x < xcnt; x++)
                {
                    Rectangle chunkRect = new Rectangle(
                        (MAX_TEXTURE_SIZE * x) / Engine.TileWidth,
                        (MAX_TEXTURE_SIZE * y) / Engine.TileHeight,
                        Math.Min(
                            (chunksX > 1 ? MAX_TEXTURE_SIZE : totalWidth) / Engine.TileWidth, 
                            ((totalWidth - (x * MAX_TEXTURE_SIZE)) / Engine.TileWidth)),
                       Math.Min(
                            (chunksY > 1 ? MAX_TEXTURE_SIZE : totalHeight) / Engine.TileHeight, 
                            ((totalHeight - (y * MAX_TEXTURE_SIZE)) / Engine.TileHeight))
                            );

                    chunks[y, x] = GenerateMapImage(chunkRect, tilesets, renderCharacters, renderCharacterLabels);
                }

            return chunks;
        }

        private Texture2D GenerateMapImage(Rectangle camera, List<Tileset> tilesets, bool renderCharacters, bool renderCharacterLabels)
        {
            RenderTarget2D rt = new RenderTarget2D(
                graphics, 
                camera.Width * Engine.TileWidth, 
                camera.Height * Engine.TileHeight);

            graphics.SetRenderTarget(rt);

            SpriteBatch.Begin();

            //SpriteBatch.Begin(
            //    SpriteSortMode.Deferred,
            //    BlendState.AlphaBlend,
            //    SamplerState.PointClamp,
            //    null,
            //    null,
            //    null,
            //    Matrix.Identity);

            //reverse the layers because the first layer should be a the top, but if 
            //we loop through them that way it'll be drawn under the rest of the layers
            for (int i = layers.Count - 1, cnt = -1; i > cnt; i--)
                layers[i].DrawExport(camera, SpriteBatch, tilesets);

            //DrawDisplay();
            SpriteBatch.End();

            graphics.SetRenderTarget(null);

            return rt;
        }

        public GDIBitmap Combine(Texture2D[,] chunks, int finalWidth, int finalHeight)
        {
            if (chunks.GetLength(0) == 1 && chunks.GetLength(1) == 1)
                return (GDIBitmap)chunks[0, 0].ToImage();

            GDIBitmap finalImage = new GDIBitmap(finalWidth, finalHeight);
            
            using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(finalImage))
            {
                g.Clear(System.Drawing.Color.Black);

                for (int y = 0, ycnt = chunks.GetLength(0); y < ycnt; y++)
                    for (int x = 0, xcnt = chunks.GetLength(1); x < xcnt; x++)
                    {
                        System.Drawing.Image image = chunks[y, x].ToImage();
                         g.DrawImage(
                            image, 
                            new System.Drawing.Rectangle(
                                x * MAX_TEXTURE_SIZE, 
                                y * MAX_TEXTURE_SIZE, 
                                image.Width, 
                                image.Height));
                    }
            }

            return finalImage;
        }
    }
}
